/*
* Copyright (C) 2008-2009 J-P Nurmi jpnurmi@gmail.com
*
* This library is free software; you can redistribute it and/or modify it
* under the terms of the GNU Lesser General Public License as published by
* the Free Software Foundation; either version 2 of the License, or (at your
* option) any later version.
*
* This library is distributed in the hope that it will be useful, but WITHOUT
* ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or
* FITNESS FOR A PARTICULAR PURPOSE.  See the GNU Lesser General Public
* License for more details.
*
* $Id: ircchannelbuffer.cpp 581 2009-05-19 16:40:38Z jpnurmi $
*/

#include "ircchannelbuffer.h"
#include "ircbuffer_p.h"
#include "irc.h"
#include <QtDebug>

bool userLessThan(const QByteArray& a, const QByteArray& b)
{
    char aa = a.at(0);
    char bb = b.at(0);
    if (aa != bb)
    {
        if (aa == '@' || bb == '@')
            return aa == '@';
        else if (aa == '+')
            return bb != '@';
        else if (bb == '+')
            return bb == '@';
    }
    return qstrcmp(a, b) < 0;
}

namespace Irc
{
    class ChannelBufferPrivate : public BufferPrivate
    {
        Q_DECLARE_PUBLIC(ChannelBuffer)

    public:
        ChannelBufferPrivate();

        void addName(const QByteArray& name);
        void removeName(const QByteArray& name);

        QByteArray key;
        QByteArray mode;
        QByteArray topic;
        QList<QByteArray> names;
    };

    ChannelBufferPrivate::ChannelBufferPrivate() :
        key(), mode(), topic(), names()
    {
    }

    void ChannelBufferPrivate::addName(const QByteArray& name)
    {
        QList<QByteArray>::iterator it = qLowerBound(names.begin(), names.end(), name);
        if (it == names.end() || *it != name)
            names.insert(it, name);
    }

    void ChannelBufferPrivate::removeName(const QByteArray& name)
    {
        QList<QByteArray>::iterator it = qBinaryFind(names.begin(), names.end(), name);
        if (it != names.end())
            names.erase(it);
    }

    /*!
        Constructs a new IRC channel buffer with \a session as parent.
     */
    ChannelBuffer::ChannelBuffer(const QByteArray& channel, const QByteArray& key, Session* session) : 
        Buffer(channel, session, *new ChannelBufferPrivate)
    {
        setKey(key);
    }

    /*!
        Destructs the IRC channel buffer.
     */
    ChannelBuffer::~ChannelBuffer()
    {
    }

    /*!
        Returns the key.
     */
    QByteArray ChannelBuffer::key() const
    {
        Q_D(const ChannelBuffer);
        return d->key;
    }

    /*!
        Sets the \a key.
     */
    void ChannelBuffer::setKey(const QByteArray& key)
    {
        Q_D(ChannelBuffer);
        d->key = key;
        raw("MODE " + name() + " +k " + key);
    }

    /*!
        Returns the mode.
     */
    QByteArray ChannelBuffer::mode() const
    {
        Q_D(const ChannelBuffer);
        return d->mode;
    }

    /*!
        Sets the \a mode.
     */
    void ChannelBuffer::setMode(const QByteArray& mode)
    {
        Q_D(ChannelBuffer);
        d->mode = mode;
        raw("MODE " + name() + " " + mode);
    }

    /*!
        Returns the topic.
     */
    QByteArray ChannelBuffer::topic() const
    {
        Q_D(const ChannelBuffer);
        return d->topic;
    }

    /*!
        Sets the \a topic.
     */
    void ChannelBuffer::setTopic(const QByteArray& topic)
    {
        Q_D(ChannelBuffer);
        d->topic = topic;
        raw("TOPIC " + name() + " :" + topic);
    }

    /*!
        Returns names on the channel.
     */
    QList<QByteArray> ChannelBuffer::names() const
    {
        Q_D(const ChannelBuffer);
        return d->names;
    }

    /*!
        Parts with \a reason.
     */
    void ChannelBuffer::part(const QByteArray& reason)
    {
        raw("PART " + name() + " " + reason);
    }

    /*!
        Kicks \a user with \a reason.
     */
    void ChannelBuffer::kick(const QByteArray& user, const QByteArray& reason)
    {
        raw("KICK " + name() + " " + user + " :" + reason);
    }

    /*!
        Invites \a user.
     */
    void ChannelBuffer::invite(const QByteArray& user)
    {
        raw("INVITE " + user + " " + name());
    }

    /*!
        \reimp
     */
    qint64 ChannelBuffer::writeData(const char* data, qint64 maxSize)
    {
        Q_D(ChannelBuffer);
        QByteArray line = QByteArray::fromRawData(data, maxSize);
        if (d->code != 0)
        {
            switch (d->code)
            {
            case Irc::Rfc::RPL_NAMREPLY:
                {
                    QList<QByteArray> names = line.mid(line.indexOf(':', 1) + 1).split(' ');
                    foreach (QByteArray name, names)
                    {
                        if (name.startsWith('@') || name.startsWith('+'))
                            name.remove(0, 1);
                        d->addName(name);
                    }
                    qDebug() << name() << "NAMES:" << d->names;
                }
                break;

            default:
                break;
            }
        }
        else if (!qstrcmp(d->command, "JOIN"))
        {
            d->addName(d->prefix);
            emit joined(d->prefix);
        }
        else if (!qstrcmp(d->command, "PART"))
        {
            d->removeName(d->prefix);
            qDebug() << Buffer::name() << d->prefix << "PARTED:" << line.mid(line.indexOf(':', 1) + 1);
            emit parted(d->prefix, line.mid(line.indexOf(':', 1) + 1));
        }
        else if (!qstrcmp(d->command, "TOPIC"))
        {
            d->topic = line.mid(line.indexOf(':', 1) + 1);
            qDebug() << d->prefix << "TOPIC:" << d->topic;
            emit topicChanged(d->prefix, d->topic);
        }
        return Buffer::writeData(data, maxSize);
    }
}

#include "moc_ircchannelbuffer.cpp"
